/**
 * 代码编译模块
 * 负责对收到的代码进行编译生成可执行文件
 * 通过fork创建子进程去编译代码，并通过重定向，把编译结果保存到一个临时文件中返回
 */
#pragma once
#include<iostream>
#include<string>
#include"../Comm/Detail.hpp"
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>


namespace OnlineOJ
{
    namespace ns_Compiler
    {
        //编译代码
        class Compiler
        {
        public:
            //传入文件名，进行编译
            //成功返回true，失败返回false
            static bool Compile(const std::string& filename) 
            {
                //创建子进程
                pid_t pid=fork();
                if(pid<0)
                {
                    LOG(ERROR,"编译时，创建子进程失败\n");
                    return false;
                }
                else if(pid==0)//子进程，调用编译器完成编译
                {
                    umask(0);//设置默认权限掩码
                    //打开一个用于保存错误信息的临时文件
                    int _stderr=open(ns_Detail::PathUtil::CompileError(filename).c_str(),O_CREAT|O_WRONLY,0644);
                    if(_stderr<0)
                    {
                        LOG(WARNING,"编译时，打开%s文件失败\n",ns_Detail::PathUtil::CompileError(filename).c_str());
                        exit(1);
                    }
                    //把标准错误(2)重定向到_stderr
                    dup2(_stderr,2);

                    //程序替换执行g++
                    execlp("g++","g++","-o",ns_Detail::PathUtil::Exe(filename).c_str()\
                            ,ns_Detail::PathUtil::Source(filename).c_str(),\
                            "-D","COMPILER_ONLINE","-std=c++11",nullptr);
                    LOG(ERROR,"编译时，g++编译器调用失败\n");
                    exit(2);
                }
                else//父进程,获取编译结果
                {
                    //等待子进程
                    waitpid(pid,nullptr,0);
                    //通过是否生成可执行文件，判断子进程是否编译完成
                    std::string pathname=ns_Detail::PathUtil::Exe(filename);
                    if(ns_Detail::FileUtil::IsFileExists(pathname))
                    {
                        LOG(INFO,"编译时，%s文件编译成功\n",ns_Detail::PathUtil::Source(filename).c_str());
                        return true;
                    }
                }
                LOG(ERROR,"编译时，%s文件编译失败，没有形成可执行程序\n",ns_Detail::PathUtil::Source(filename).c_str());
                return false;
            } 
        };
    }
}